﻿
/******************************************************
* author :  jilonglv
* email  : jilonglv@gmail.com
* function:
* history:  created by jilonglv 2020/10/14 11:29:35
* clrversion :4.0.30319.42000
******************************************************/

namespace Commnunications
{
    using System;
    using System.Runtime.InteropServices;

    public class CommandBuilder
    {
        byte[] _buffer = new byte[2048];
        int _len { get; set; }
        /// <summary>
        /// 当前字节个数
        /// </summary>
        public int Length { get => _len; }
        public byte[] Content => _buffer;
        public CommandBuilder() { }
        public CommandBuilder(int bufferSize)
        {
            _buffer = new byte[bufferSize];
        }

        /// <summary>
        /// 将对象序列化到当前命令中，并返回其实地址
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public int Payload(object data)
        {
            var offset = _len;
            var bytes = StructToBytes(data, -1);
            Array.Copy(bytes, 0, _buffer, offset, bytes.Length);
            _len += bytes.Length;
            return offset;
        }
        /// <summary>
        /// 将对象序列化到当前命令中，并返回其实地址(注：不能使用嵌套结构体）
        /// </summary>
        /// <param name="bytes">字节数组</param>
        /// <param name="byteOffset">bytes的其实偏移地址，默认为0</param>
        /// <param name="len">bytes的长度，默认使用bytes字节个数</param>
        /// <returns></returns>
        public int Payload(byte[] bytes, int byteOffset = 0, int len = -1)
        {
            var offset = _len;
            if (len <= 0)
                len = bytes.Length;

            Array.Copy(bytes, byteOffset, _buffer, offset, len);
            _len += len;
            return offset;
        }
        /// <summary>
        /// 将对象序列化到当前命令中，并返回其实地址(注：不能使用嵌套结构体）
        /// </summary>
        /// <param name="bytes">字节</param>
        /// <returns></returns>
        public int PayloadByte(byte byteData)
        {
            var offset = _len;
            _buffer[offset] = byteData;
            _len += 1;
            return offset;
        }
        /// <summary>
        /// 调整
        /// </summary>
        /// <param name="action"></param>
        public void Configure(Action<byte[]> action)
        {
            action(_buffer);
        }
        /// <summary>
        /// 调整数据并移动偏移个数
        /// </summary>
        /// <param name="action"></param>
        /// <param name="offset">偏移个数</param>
        public void Configure(Action<byte[]> action,int offset)
        {
            action(_buffer);
            _len += offset;
        }
        /// <summary>
        /// 结构体转byte数组(注：不能使用嵌套结构体）
        /// </summary>
        /// <param name="structObj">要转换的结构体</param>
        /// <returns>转换后的byte数组</returns>
        public static byte[] StructToBytes(object structObj, int len = -1)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(structObj);
            int structSize = size;
            if (len >= 0)
                size = len;

            //创建byte数组
            byte[] bytes = new byte[size];
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(structSize);

            try
            {
                //将结构体拷到分配好的内存空间
                Marshal.StructureToPtr(structObj, structPtr, false);
                //从内存空间拷到byte数组
                Marshal.Copy(structPtr, bytes, 0, size);
            }
            finally
            {
                //释放内存空间
                Marshal.FreeHGlobal(structPtr);
            }
            //返回byte数组
            return bytes;
        }
        /// <summary>
        /// byte数组转结构体
        /// </summary>
        /// <param name="bytes">byte数组</param>
        /// <param name="type">结构体类型</param>
        /// <returns>转换后的结构体</returns>
        public static object BytesToStuct(byte[] bytes, Type type, int offset = 0, bool bigLittleConvert = false)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(type);
            int structSize = size;
            //byte数组长度小于结构体的大小
            if (size > bytes.Length - offset)
            {
                //返回空
                return null;
            }
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(structSize);
            //将byte数组拷到分配好的内存空间
            try
            {
                var buffer = bytes;
                if (bigLittleConvert)
                {
                    buffer = new byte[size];
                    Array.Copy(bytes, offset, buffer, 0, size);
                    Array.Reverse(buffer);
                    offset = 0;
                }

                Marshal.Copy(buffer, offset, structPtr, size);
                //将内存空间转换为目标结构体
                object obj = Marshal.PtrToStructure(structPtr, type);
                return obj;
            }
            catch (Exception)
            {
            }
            finally
            {
                //释放内存空间
                Marshal.FreeHGlobal(structPtr);
            }
            //返回结构体
            return null;
        }
        public static T BytesToStuct<T>(byte[] bytes, int offset = 0, bool bigLittleConvert = false)
        {
            var type = typeof(T);
            var obj = default(T);
            try
            {
                obj = (T)BytesToStuct(bytes, type, offset, bigLittleConvert);
            }
            catch (Exception)
            {
            }
            return obj;
        }
        public static string BytesToString(byte[] bytes, int size)
        {
            //得到结构体的大小
            //byte数组长度小于结构体的大小
            if (size > bytes.Length)
            {
                //返回空
                return null;
            }
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //将内存空间转换为目标结构体
            string obj = null;
            try
            {
                obj = Marshal.PtrToStringAnsi(structPtr, size);
            }
            catch (Exception)
            {
            }
            finally
            {
                //释放内存空间
                Marshal.FreeHGlobal(structPtr);
            }
            //返回结构体
            return obj;
        }
        public static string ToHexString(byte[] buffer, int offset = 0, int len = 0)
        {
            var sb = new System.Text.StringBuilder();
            //foreach (var t in buffer)
            if (len <= 0)
                len = buffer.Length;

            for (int i = offset; i < len; i++)
            {
                sb.Append(buffer[i].ToString("x02")/*+" "*/);
            }
            return sb.ToString();
        }
    }
}
